import socket
import threading
import netaddr
import netifaces
from netaddr import IPNetwork

from Python.T5.Flight_Multi import start_config

output_mod = True  # 输出调试开关

Img_Size = 10240


class connection:
    # 本机IP和端口地址
    cmd_ip = None
    cmd_port = 8889
    cmd_sock = None

    # 状态流端口
    message_port = 8890
    message_sock = None

    # 视频流端口
    video_port = 11111
    video_sock = None

    # 无人机IP地址
    ip_list = []

    def __init__(self):
        """
        根据config文件创建无人机连接
        """
        self.cmd_ip = (socket.gethostbyname(socket.gethostname()))
        self.cmd_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # socket for sending cmd
        self.cmd_sock.bind((self.cmd_ip, self.cmd_port))
        if output_mod:
            print(" *** Created cmd_sock {}:{}".format(self.cmd_ip, self.cmd_port))

        T_find = threading.Thread(target=self.scan_ip)
        T_find.start()
        self.send_ip()
        T_find.join()

        # 对找到的ip进行套接字创建
        self.video_sort()
        self.message_sort()

    def rec_cmd(self):
        rec = self.cmd_sock.recvfrom(1024)
        if output_mod:
            print(" *** received:{}".format(rec))
        return rec[0].decode('utf-8')

    def rec_cmd_ip(self):
        rec = self.cmd_sock.recvfrom(1024)
        if output_mod:
            print(" *** received:{}".format(rec))
        return rec

    def rec_video(self, no=0):
        """
        接收无人机视频信息流
        :param no: 无人机编号
        :return:
        """
        return self.video_sock[no].recvfrom(Img_Size)

    def rec_msg(self, no=0):
        """
        接收无人机状态信息流
        :param no: 无人机编号
        :return:
        """
        return self.message_sock.recvfrom(1024)

    def sendto(self, cmd="", no=-1):
        """
        :param cmd:指令
        :param no: 所选无人机编号,默认-1发送给所有飞机
        :return:
        """
        if no == -1:
            for i in range(len(self.ip_list)):
                ip = self.ip_list[i]
                print(" *** send '{}' to No.{}".format(cmd, i))
                self.cmd_sock.sendto(cmd.encode('gbk'), (ip, self.cmd_port))
        else:
            print(" *** send {} to No.{}".format(cmd, no))
            self.cmd_sock.sendto(cmd.encode('gbk'), (self.ip_list[no], self.cmd_port))

    def sendto_ESP32(self, cmd="", no=-1):
        """
        :param cmd:指令
        :param no: 所选无人机编号,默认-1发送给所有飞机
        :return:
        """
        cmd = "EXT " + cmd
        if no == -1:
            for i in range(len(self.ip_list)):
                ip = self.ip_list[i]
                print(" *** send {} to No.{}".format(cmd, i))
                self.cmd_sock.sendto(cmd.encode('gbk'), (ip, self.cmd_port))
        else:
            print(" *** send {} to No.{}".format(cmd, no))
            self.cmd_sock.sendto(cmd.encode('gbk'), (self.ip_list[no], self.cmd_port))

    def send(self, ip="", cmd=""):
        """
        通过本机ip向指定端口发送命令
        :param ip:目标ip
        :param cmd: 命令
        :return:
        """
        # if output_mod:
        #     print(" *** send '{}' to {}:{}".format(cmd, ip, self.cmd_port))
        self.cmd_sock.sendto(cmd.encode('gbk'), (ip, self.cmd_port))

    def scan_ip(self):
        """
        把找到的IP放到ip队列
        :return:
        """
        found = 0
        while found < start_config.flight_number:
            resp, ip = self.rec_cmd_ip()
            ip = ''.join(str(ip[0]))
            if resp.upper() == b'OK' and ip not in self.ip_list:
                found += 1
                self.ip_list.append(ip)

    def send_ip(self):
        """
        向每个可能IP发送command指令
        :return:
        """
        subnets, address = self.get_subnets()
        possible_addr = []

        for subnet, netmask in subnets:
            for ip in IPNetwork('%s/%s' % (subnet, netmask)):
                # skip local and broadcast
                if str(ip).split('.')[3] == '0' or str(ip).split('.')[3] == '255':
                    continue
                possible_addr.append(str(ip))

        for ip in possible_addr:
            if len(self.ip_list) >= start_config.flight_number:
                break
            if ip in address:
                continue
            self.send(cmd='command', ip=ip)
        # while len(self.ip_list) < start_config.flight_number:
        #     # delete already fond Tello ip
        #     for tello_host in self.ip_list:
        #         if tello_host in possible_addr:
        #             possible_addr.remove(tello_host)
        #     # skip server itself
        #     for ip in possible_addr:
        #         if ip in address:
        #             continue
        #         self.send(cmd='command', ip=ip)
        #     if len(self.ip_list) >= start_config.flight_number:
        #         break
        return self.ip_list

    def get_subnets(self):
        """
        Look through the machine's internet connection and
        returns subnet addresses and server ip
        :return: list[str]: subnets
                 list[str]: addr_list
        """
        subnets = []
        ifaces = netifaces.interfaces()
        addr_list = []
        for myiface in ifaces:
            addrs = netifaces.ifaddresses(myiface)

            if socket.AF_INET not in addrs:
                continue
            # Get ipv4 stuff
            ipinfo = addrs[socket.AF_INET][0]
            address = ipinfo['addr']
            netmask = ipinfo['netmask']
            broadcast = ipinfo['broadcast']

            # special subnet
            if self.cmd_ip is not None:
                target_broadcast = self.cmd_ip.rsplit('.', 1)[0] + '.255'
                if target_broadcast != broadcast:
                    continue

            # limit range of search. This will work for router subnets
            if netmask != '255.255.255.0':
                continue

            # Create ip object and get
            cidr = netaddr.IPNetwork('%s/%s' % (address, netmask))
            network = cidr.network
            subnets.append((network, netmask))
            addr_list.append(address)
        return subnets, addr_list

    def video_sort(self):
        """
        视频接收套接字创建
        :return:
        """
        self.video_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.video_sock.bind((self.cmd_ip, self.video_port))

    def message_sort(self):
        """
        消息接收套接字创建
        :return:
        """
        self.message_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.message_sock.bind((self.cmd_ip, self.message_port))
